#include <TripleDES.h>
#include <Padding.h>
#include <QMetaEnum>
#include <QString>
#include <QByteArray>
#include <openssl/des.h>

#define DES_BLOCK_SIZE 8

bool TripleDES::encrypt(QByteArray &in, QByteArray &out, QByteArray &key, bool enc, Mode mode)
{
    ecb_encrypt(in,out,key,enc);
}

bool TripleDES::encrypt(QByteArray &in, QByteArray &out, QByteArray &key, bool enc, Mode mode, QByteArray &iv)
{
    if(mode==cbc)
    {
        cbc_encrypt(in,out,key,ivec,enc);
    }
    else if(mode==cfb)
    {
        cfb_encrypt(in,out,key,ivec,enc);
    }
    else if(mode==ofb)
    {
        ofb_encrypt(in,out,key,ivec,enc);
    }
}

void TripleDES::ecb_encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, bool enc)
{
    qDebug()<<"enter ecb_encrypt";
    // 检查密钥合法性(只能是24字节)
    Q_ASSERT(key.size() == 24);

    //密钥置换
    DES_key_schedule sch1, sch2, sch3;
    unsigned char block_key[9];
    memset(block_key, 0, sizeof(block_key));
    unsigned char k[24];
    memcpy(k,key,24);  //把QByteArray转换成char数组
    memcpy(block_key, k+0, 8);
    DES_set_key_unchecked((const_DES_cblock*)block_key,&sch1);
    memcpy(block_key, k+8, 8);
    DES_set_key_unchecked((const_DES_cblock*)block_key,&sch2);
    memcpy(block_key, k+16, 8);
    DES_set_key_unchecked((const_DES_cblock*)block_key,&sch3);


    // 进行加解密
    if (enc)
    {
        // 进行PKCS7Padding填充
        QByteArray inTemp = Padding::PKCS7Padding(in, DES_BLOCK_SIZE);

        // 执行加密
        out.resize(inTemp.size()); // 调整输出out大小
        for (int i = 0; i < inTemp.size() / DES_BLOCK_SIZE; i++)
        {
            DES_ecb3_encrypt((const_DES_cblock*)(inTemp.constData() + i * DES_BLOCK_SIZE),
                             (DES_cblock *)(out.data() + i * DES_BLOCK_SIZE),
                             &sch1, &sch2, &sch3, DES_ENCRYPT);
        }
    }
    else
    {
        // 执行解密
        out.resize(in.size()); // 调整输出buf大小
        for (int i = 0; i < in.size() / DES_BLOCK_SIZE; i++)
        {
            DES_ecb3_encrypt((const_DES_cblock*)(in.constData() + i * DES_BLOCK_SIZE),
                             (DES_cblock *)(out.data() + i * DES_BLOCK_SIZE),
                             &sch1, &sch2, &sch3, DES_DECRYPT);
        }

        // 解除PKCS7Padding填充
        out = Padding::PKCS7UnPadding(out);
    }
}

void TripleDES::cbc_encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc)
{
    // 检查密钥合法性(只能是24字节)
    Q_ASSERT(key.size() == 24);
    Q_ASSERT(ivec.size() == 8); // 初始向量为8字节

    // 密钥置换
    DES_key_schedule sch1, sch2, sch3;
    unsigned char block_key[9];
    memset(block_key, 0, sizeof(block_key));
    unsigned char k[24];
    memcpy(k,key,24);  //把QByteArray转换成char数组
    memcpy(block_key, k+0, 8);
    DES_set_key_unchecked((const_DES_cblock*)block_key,&sch1);
    memcpy(block_key, k+8, 8);
    DES_set_key_unchecked((const_DES_cblock*)block_key,&sch2);
    memcpy(block_key, k+16, 8);
    DES_set_key_unchecked((const_DES_cblock*)block_key,&sch3);


    // 进行加解密
    QByteArray ivecTemp = ivec; // 此参数被自动修改，故使用局部变量传递

    if (enc)
    {
        // 进行PKCS7Padding填充
        QByteArray inTemp = Padding::PKCS7Padding(in, DES_BLOCK_SIZE);

        // 执行加密
        out.resize(inTemp.size()); // 调整输出buf大小
        DES_ede3_cbc_encrypt((const unsigned char *)inTemp.constData(),
                             (unsigned char *)out.data(),
                             inTemp.size(), &sch1, &sch2, &sch3,
                             (DES_cblock *)ivecTemp.data(), DES_ENCRYPT);
    }
    else
    {
        // 执行解密
        out.resize(in.size()); // 调整输出buf大小
        DES_ede3_cbc_encrypt((const unsigned char *)in.constData(),
                             (unsigned char *)out.data(),
                             in.size(), &sch1, &sch2, &sch3,
                             (DES_cblock *)ivecTemp.data(), DES_DECRYPT);

        // 解除PKCS7Padding填充
        out = Padding::PKCS7UnPadding(out);
    }
}

void TripleDES::cfb_encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc)
{
    // 检查密钥合法性(只能是24字节)
    Q_ASSERT(key.size() == 24);
    Q_ASSERT(ivec.size() == 8); // 初始向量为8字节

    // 设置key
    DES_key_schedule sch1, sch2, sch3;
    unsigned char block_key[9];
    memset(block_key, 0, sizeof(block_key));
    unsigned char k[24];
    memcpy(k,key,24);  //把QByteArray转换成char数组
    memcpy(block_key, k+0, 8);
    DES_set_key_unchecked((const_DES_cblock*)block_key,&sch1);
    memcpy(block_key, k+8, 8);
    DES_set_key_unchecked((const_DES_cblock*)block_key,&sch2);
    memcpy(block_key, k+16, 8);
    DES_set_key_unchecked((const_DES_cblock*)block_key,&sch3);

    // 进行加解密
    QByteArray ivecTemp = ivec; // 此参数被自动修改，故使用局部变量传递
    int encVal = enc ? DES_ENCRYPT : DES_DECRYPT;
    out.resize(in.size()); // 调整输出buf大小
    DES_ede3_cfb_encrypt((const unsigned char *)in.constData(),
                         (unsigned char *)out.data(),
                         8, in.size(), &sch1, &sch2, &sch3,
                         (DES_cblock *)ivecTemp.data(), encVal);
}


void TripleDES::ofb_encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc)
{
    // 检查密钥合法性(只能是24字节)
    Q_ASSERT(key.size() == 24);
    Q_ASSERT(ivec.size() == 8); // 初始向量为8字节

    Q_UNUSED(enc)

    // 设置key
    DES_key_schedule sch1, sch2, sch3;
    unsigned char block_key[9];
    memset(block_key, 0, sizeof(block_key));
    unsigned char k[24];
    memcpy(k,key,24);  //把QByteArray转换成char数组
    memcpy(block_key, k+0, 8);
    DES_set_key_unchecked((const_DES_cblock*)block_key,&sch1);
    memcpy(block_key, k+8, 8);
    DES_set_key_unchecked((const_DES_cblock*)block_key,&sch2);
    memcpy(block_key, k+16, 8);
    DES_set_key_unchecked((const_DES_cblock*)block_key,&sch3);


    // 进行加解密
    int num = 0;
    QByteArray ivecTemp = ivec; // 此参数被自动修改，故使用局部变量传递
    out.resize(in.size()); // 调整输出buf大小
    DES_ede3_ofb64_encrypt((const unsigned char *)in.constData(),
                         (unsigned char *)out.data(),
                         in.size(), &sch1, &sch2, &sch3,
                         (DES_cblock *)ivecTemp.data(), &num);
}
